using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Resources;
using System.IO;
using Modelica.Annotations;


namespace DrawToolsLib {
    /// <summary>
    ///  PolyLine graphics object.
    /// </summary>
    public class GraphicsPolygon : GraphicsBase {
        #region Class Members
        private Polygon src;  // Modelica Polygon

        // This class member contains all required geometry.
        // It is ready for drawing and hit testing.
        protected PathGeometry pathGeometry;

        static Cursor handleCursor;

        #endregion Class Members

        #region Constructors

        public GraphicsPolygon(Polygon src) {
            if (src != null) {
                this.src = src;
            } else {
                this.src = new Polygon();
            }

            this.graphicsActualScale = 1.0;
        }

        public GraphicsPolygon()
            : this(new Polygon()) {
        }

        public GraphicsPolygon(Point[] points, double lineWidth, Color objectColor,
            double actualScale, CoordinateSystem cs)
            : this() {
            this.src.LineThickness = lineWidth;
            this.src.FillColor = objectColor;
            this.graphicsActualScale = actualScale;
            this.ParentCS = cs;
            this.points = points;
            ResetGraphics();
        }

        static GraphicsPolygon() {
            handleCursor = Cursors.UpArrow;
        }

        #endregion Constructors

        #region Properties

        protected override double graphicsLineWidth {
            get {
                return src.LineThickness;
            }
            set {
                src.LineThickness = value;
                RefreshDrawing();
            }
        }

        protected override Color graphicsObjectColor {
            get {
                return Color.FromRgb(src.FillColor.R, src.FillColor.G, src.FillColor.B);
            }
            set {
                src.FillColor = value;
                RefreshDrawing();
            }
        }

        public override GraphicItem Source {
            get {
                return src;
            }
        }

        //Points from pathGeometry, including StartPoint
        protected Point[] points {
            get {
                Point[] p = new Point[this.src.Points.Length];
                for (int i = 0; i < this.src.Points.Length; i++) {
                    p[i].X = this.src.Points[i].X - ex.X1;
                    p[i].Y = ex.Y2 - this.src.Points[i].Y;
                }

                return p;
            }

            set {
                this.src.Points = new Point[value.Length];
                for (int i = 0; i < value.Length; i++) {
                    this.src.Points[i].X = value[i].X + ex.X1;
                    this.src.Points[i].Y = ex.Y2 - value[i].Y;
                }
            }
        }

        #endregion Properties

        #region Other Functions

        protected override void ResetGraphics() {
            MakeGeometryFromPoints(this.points);
        }

        /// <summary>
        /// Convert geometry to array of points.
        /// </summary>
        void MakePoints() {
            Point[] privPoints = new Point[pathGeometry.Figures[0].Segments.Count + 1];

            privPoints[0] = pathGeometry.Figures[0].StartPoint;

            for (int i = 0; i < pathGeometry.Figures[0].Segments.Count; i++) {
                privPoints[i + 1] = ((LineSegment)(pathGeometry.Figures[0].Segments[i])).Point;
            }
            points = privPoints;
        }

        /// <summary>
        /// Return array of points.
        /// </summary>
        public Point[] GetPoints() {
            return points;
        }

        /// <summary>
        /// Convert array of points to geometry.
        /// </summary>
        void MakeGeometryFromPoints(Point[] points) {
            pathGeometry = new PathGeometry();

            PathFigure figure = new PathFigure();
            figure.IsClosed = true;

            if (points.Length >= 1) {
                figure.StartPoint = points[0];
            }

            pathGeometry.Figures.Add(figure);

            for (int i = 1; i < points.Length; i++) {
                AddPoint(points[i]);
            }
        }

        /// <summary>
        /// Add new point (line segment)
        /// </summary>
        public void AddPoint(Point point) {
            LineSegment segment = new LineSegment(point, true);
            segment.IsSmoothJoin = true;

            pathGeometry.Figures[0].Segments.Add(segment);
        }

        #endregion Other Functions

        #region Overrides

        /// <summary>
        /// Draw object
        /// </summary>
        public override void Draw(DrawingContext drawingContext) {
            if (drawingContext == null) {
                throw new ArgumentNullException("drawingContext");
            }

            if (pathGeometry == null) {
                MakeGeometryFromPoints(this.points);
            }

            drawingContext.DrawGeometry(
                Brushes.Turquoise,
                new Pen(new SolidColorBrush(ObjectColor), ActualLineWidth),
                pathGeometry);

            base.Draw(drawingContext);

            MakePoints();
        }

        /// <summary>
        /// Test whether object contains point
        /// </summary>
        public override bool Contains(Point point) {
            return pathGeometry.FillContains(point) ||
                pathGeometry.StrokeContains(new Pen(Brushes.Black, LineHitTestWidth), point);
        }

        /// <summary>
        /// Get number of handles
        /// </summary>
        public override int HandleCount {
            get {
                return pathGeometry.Figures[0].Segments.Count + 1;
            }
        }


        /// <summary>
        /// Get handle point by 1-based number
        /// </summary>
        public override Point GetHandle(int handleNumber) {
            if ((handleNumber < 1) || (handleNumber > HandleCount)) {
                throw new Exception("Invalid handle number in Polygon GetHandle.");
            }

            if (handleNumber == 1) {
                return pathGeometry.Figures[0].StartPoint;
            } else {
                return ((LineSegment)(pathGeometry.Figures[0].Segments[handleNumber - 2])).Point;
            }
        }

        /// <summary>
        /// Get cursor for the handle
        /// </summary>
        public override Cursor GetHandleCursor(int handleNumber) {
            return handleCursor;
        }

        /// <summary>
        /// Move handle to new point (resizing).
        /// handleNumber is 1-based.
        /// </summary>
        public override void MoveHandleTo(Point point, int handleNumber) {
            if (handleNumber == 1) {
                pathGeometry.Figures[0].StartPoint = point;
            } else {
                ((LineSegment)(pathGeometry.Figures[0].Segments[handleNumber - 2])).Point = point;
            }

            RefreshDrawing();
        }


        /// <summary>
        /// Move object
        /// </summary>
        public override void Move(double deltaX, double deltaY) {
            pathGeometry.Figures[0].StartPoint += new Vector(deltaX, deltaY);

            for (int i = 0; i < pathGeometry.Figures[0].Segments.Count; i++) {
                ((LineSegment)(pathGeometry.Figures[0].Segments[i])).Point += new Vector(deltaX, deltaY);
            }

            RefreshDrawing();
        }


        /// <summary>
        /// Hit test.
        /// Return value: -1 - no hit
        ///                0 - hit anywhere
        ///                > 1 - handle number
        /// </summary>
        public override int MakeHitTest(Point point) {
            if (IsSelected) {
                for (int i = 1; i <= HandleCount; i++) {
                    if (GetHandleRectangle(i).Contains(point))
                        return i;
                }
            }

            if (Contains(point))
                return 0;

            return -1;
        }

        public override object Clone() {
            GraphicsPolygon g = new GraphicsPolygon((Polygon)this.src.Clone());
            g.selected = this.selected;
            g.ActualScale = this.ActualScale;
            return g;
        }

        #endregion Overrides

        /// <summary>
        /// Test whether object intersects with rectangle
        /// </summary>
        public override bool IntersectsWith(Rect rectangle) {
            RectangleGeometry rg = new RectangleGeometry(rectangle);

            PathGeometry p = Geometry.Combine(rg, pathGeometry, GeometryCombineMode.Intersect, null);

            return (!p.IsEmpty());
        }

        /// <summary>
        /// Remove the last point created in the final click+doubleclick
        /// </summary>
        internal void RemoveLastPoint() {
            pathGeometry.Figures[0].Segments.RemoveAt(pathGeometry.Figures[0].Segments.Count - 1);

            //MakePoints();   // keep points array up to date
        }

        /// <summary>
        /// Draw border for selected object.
        /// </summary>
        public override void DrawBorder(DrawingContext drawingContext) {
            Rect r = new Rect(pathGeometry.Bounds.X, pathGeometry.Bounds.Y,
                pathGeometry.Bounds.Width, pathGeometry.Bounds.Height);
            drawingContext.DrawRectangle(null, borderPen, r);
        }
    }
}
